package com.cooqe.collection_snmp.snmpfinder;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cooqe.collection_snmp.model.SnmpParameter;
import com.cooqe.collection_snmp.utils.SnmpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.snmp4j.PDU;
import org.snmp4j.ScopedPDU;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.VariableBinding;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.*;

/**
 * @author 折戟沉沙铁未销
 * @version V1.0
 * @date 2023/11/16-2023
 * @Description: snmp getBulk 方式进行指标的采集
 */
@Component
@Slf4j
@SuppressWarnings("all")
public class SnmpBulkHandle {

    /**
     * snmp getBulk 操作，进行oid 指标值的获取
     * {
     * "method": "getBulk",
     * "oids": "1.3.6.1.2.1.4.22.1.1,1.3.6.1.2.1.4.22.1.2,1.3.6.1.2.1.4.22.1.3,1.3.6.1.2.1.4.22.1.4"
     * }
     */
    public static Map<String, List<String>> snmpBulk(JSONObject collectParamsObj, SnmpParameter snmpParameter, Target target, Snmp snmp) throws Exception {
        //获取参数 oid
        String oids = collectParamsObj.getString(SnmpUtil.OIDS);
        String[] oidArrays = oids.split(SnmpUtil.REGEX_SEGMENT);

        //定义返回结果
        Map<String, List<String>> resultMap = new HashMap<>();

        //snmpGetBulk 处理
        List<String> oidList = Arrays.asList(oidArrays);
        Map<String, Map<String, String>> datas = new HashMap<>();
        snmpGetBulk(target, datas, oidList, oidArrays, snmp);

        /**
         * 最终得到的数据大概样子：
         * datas: {
         * 	    1.3.6.1.2.1.2.2.1.6: {
         * 	            1 : F6-B5-BB-00-C7-31,
         * 	            2 : F6-B5-BB-00-C7-32,
         * 	            3 : F6-B5-BB-00-C7-33,
         * 	            4 : F6-B5-BB-00-C7-34,
         * 	            5 : F6-B5-BB-00-C7-35,
         * 	            6 : F6-B5-BB-00-C7-36
         *      },
         *
         * 	    snmp_index: { 1:1 , 2:2, 3:3, 4:4, 5:5, 6:6 }
         * }
         */

        //处理返回结果
        if (!datas.isEmpty()) {
            Set<String> keySet = datas.keySet();
            for (String next : keySet) {
                if (StringUtils.isNotBlank(next)) {
                    try {
                        Collection<String> collection = datas.get(next).values();
                        resultMap.put(next, new ArrayList<>(collection));
                    } catch (Exception e) {
                        log.error(e.getMessage());
                    }
                }
            }
        }
        //采集数据为空
        else {
            log.info("SNMP get no datas with bulk operation.Reference parameters " + JSON.toJSONString(collectParamsObj));
        }
        return resultMap;
    }

    /**
     * getBulk 请求
     */
    private static void snmpGetBulk(Target target, Map<String, Map<String, String>> datas,
                                    Collection<String> loopOids, String[] rootOidArray, Snmp snmp) throws Exception {
        //定义 PDU 对象
        PDU requestPDU = (target.getVersion() == SnmpConstants.version3) ? (new ScopedPDU()) : (new PDU());
        for (String oid : loopOids) {
            requestPDU.add(new VariableBinding(new OID(oid)));
        }
        //设置 pdu 的 type=getBulk
        requestPDU.setType(PDU.GETBULK);
        //设置
        requestPDU.setMaxRepetitions(10);
        requestPDU.setNonRepeaters(0);

        // 用于下次循环的oid
        Map<String, String> currentOids = new HashMap<>();
        for (String rootOID : rootOidArray) {
            if (!datas.containsKey(rootOID)) {
                //每一个oid 都对应一个 hashMap 的数据
                datas.put(rootOID, new LinkedHashMap<>());
            }
            currentOids.put(rootOID, rootOID);
        }
        if (!datas.containsKey("snmp_index")) {
            datas.put("snmp_index", new LinkedHashMap<>());
        }

        //请求处理
        try {
            //发送请求
            ResponseEvent responseEvent = snmp.send(requestPDU, target);
            PDU response = responseEvent.getResponse();

            //校验返回结果中错误信息
            if (responseEvent.getResponse() == null) {
                throw new RuntimeException("responseEvent is null error");
            }
            if (responseEvent.getError() != null) {
                throw responseEvent.getError();
            }
            if (responseEvent.getResponse().getErrorStatus() != PDU.noError) {
                throw new RuntimeException("responseEvent.getResponse().getErrorStatus() is error");
            }

            // 获取响应
            Vector<? extends VariableBinding> bindings = responseEvent.getResponse().getVariableBindings();
            // get bullk返回块数据，所以需要和原始OID进行挨个对比，如果当前返回块没有原始OID的数据，那么就丢弃当前OID，
            // 直到所有的原始OID都 walk结束。
            List<String> overRootOids = new ArrayList<>(rootOidArray.length);
            for (String rootOid : rootOidArray) {
                boolean isEnd = true;  // 标志当前rootOid是否walk结束
                for (VariableBinding bind : bindings) {
                    String key = bind.getOid().toString();
                    if (StringUtils.startsWith(key, rootOid) && !SnmpUtil.isEndWalk(bind, new OID(rootOid), response)) {
                        //walk 未结束
                        isEnd = false;
                        // 请求结果
                        String value = String.valueOf(bind.getVariable());
                        //将rootOid之后的内容截取出来,作为一个索引
                        String index = key.substring(rootOid.length() + 1);
                        if (!datas.get("snmp_index").containsKey(index)) {
                            datas.get("snmp_index").put(index, index);
                        }
                        datas.get(rootOid).put(index, value);

                        currentOids.put(rootOid, key);
                    }
                }
                if (!isEnd) {
                    overRootOids.add(rootOid);
                }
            }

            if (overRootOids.isEmpty()) return;
            if (currentOids.isEmpty()) return;

            String[] loopRootOIDs = new String[overRootOids.size()];
            snmpGetBulk(target, datas, currentOids.values(), overRootOids.toArray(loopRootOIDs), snmp);
        } catch (IOException e) {
            log.error(e.getMessage());
            throw e;
        }
    }


    public static void main(String[] args) {
        String request="1.3.6.1.2.1.2.2.1.6.58";
        OID requestOID = new OID(request);

        String response="1.3.6.1.2.1.2.2.1.6.58.1";
        OID responseOID = new OID(response);

        //int i = requestOID.leftMostCompare(requestOID.size(), responseOID);
        //int i = responseOID.compareTo(requestOID);
        System.out.println(requestOID.size());  //0  false
        System.out.println(responseOID.size());  //0  false


        //{ 1:1 , 2:2, 3:3, 4:4, 5:5, 6:6 }
        Map<String,String> map1=new LinkedHashMap<>();
        map1.put("1","1");
        map1.put("2","2");
        map1.put("3","3");
        map1.put("4","4");
        map1.put("5","5");
        map1.put("6","6");

        Map<String,String> map2=new LinkedHashMap<>();
        map2.put("1","Enthernet-31");
        map2.put("2","Enthernet-32");
        map2.put("3","Enthernet-33");
        map2.put("4","Enthernet-34");
        map2.put("5","Enthernet-35");
        map2.put("7","Enthernet-36");
        map2.put("6","Enthernet-36");

        Map<String, Map<String, String>> datas = new HashMap<>();
        datas.put("snmp-index",map1);
        datas.put("1.3.6.1.2.1.2.2.1.6.18",map2);

        Map<String, List<String>> resultMap = new HashMap<>();
        if (!datas.isEmpty()) {
            Set<String> keySet = datas.keySet();
            for (String next : keySet) {
                if (StringUtils.isNotBlank(next)) {
                    try {
                        Collection<String> collection = datas.get(next).values();
                        resultMap.put(next, new ArrayList<>(collection));
                    } catch (Exception e) {
                        log.error(e.getMessage());
                    }
                }
            }
        }
        System.out.println(resultMap);
    }
}